import os
import traceback
from typing import Optional, Awaitable
import functools

import tornado.web
import tornado.httpclient
import logging
import json
import functools
from tornado.httputil import HTTPHeaders

from configs import config, errorCode, sms_config, errorMsg, const
from utils import smsSDK, util
from . import swagger_model
from models import execute
from utils.sessions import SessionManager, Session


class BaseHandler(tornado.web.RequestHandler):
    # sessions = set()

    def auth(func):
        @functools.wraps(func)
        def wrapper(self, *args, **kwargs):
            self.is_auth = True
            if self.current_user is None:
                return self.write_error(errorCode.NOT_LOGIN, errorMsg.NOT_LOGIN_MSG, status_code=200)
            return func(self, *args, **kwargs)

        return wrapper

    def set_default_headers(self):

        self.set_header("Access-Control-Allow-Credentials", "true")
        self.set_header("content-type", "Application/json")
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header("Access-Control-Allow-Headers", "x-requested-with, Content-Type, user_token")
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')

    def options(self):
        self.set_status(204)
        self.finish()

    def initialize(self, *args, **kwargs):
        self.session = None
        self.is_auth = False
        self.db = config.pool
        self.redis = config.redis
        self.logger: logging.Logger = self.application.logger

    async def prepare(self):
        self.logger.info("%s %s" % (self.request.method, self.request.path))
        self.logger.debug(self.request.body)
        self.logger.debug(self.request.query)

    def on_finish(self):
        # 结束日志
        self.logger.info("%s %s %d %d ms" % (
            self.request.method, self.request.path, self._status_code, self.request.request_time() * 1000))

    def write_json(self, data=None, status=errorCode.OK, status_code=200, kwargs=None):
        self.set_status(status_code)
        tmp = {'status': status, 'desc': 'OK', 'data': data or []}
        if kwargs:
            tmp.update(kwargs)
        self.logger.info(tmp)
        self.write(util.json_encode(tmp))
        self.finish()
        return

    def write_error(self, status, desc='Error', data=None, status_code=200, kwargs=None):
        self.set_status(status_code)
        tmp = {'status': status, 'desc': desc, 'data': data or []}
        if kwargs:
            tmp.update(kwargs)
        self.logger.debug(tmp)
        self.write(util.json_encode(tmp))
        self.finish()
        return

    @staticmethod
    async def send_get_request(url) -> Optional[tornado.httpclient.HTTPResponse]:
        http_client = tornado.httpclient.AsyncHTTPClient()
        try:
            response: tornado.httpclient.HTTPResponse = await http_client.fetch(url)
        except Exception as e:
            logging.error(e)
            return None
        finally:
            http_client.close()
        return response

    @staticmethod
    async def send_post_request(url, body, headers: dict = None) -> Optional[tornado.httpclient.HTTPResponse]:
        http_client = tornado.httpclient.AsyncHTTPClient()
        if headers is None:
            headers = HTTPHeaders({"content-type": "Application/json"})
        try:
            response: tornado.httpclient.HTTPResponse = await http_client.fetch(
                tornado.httpclient.HTTPRequest(url,
                                               method="POST",
                                               headers=headers,
                                               body=json.dumps(body)))
        except Exception as e:
            logging.error(e)
            return None
        finally:
            http_client.close()
        return response

    def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]:
        pass

    def get_current_user(self) -> Optional[int]:
        if not self.is_auth:
            return None
        session = self.application.session_manager.get(self)
        if session is not None:
            self.session = session
            return session.get('uid', None)
        return None

    def save_to_session(self, v, k='uid'):
        if self.session is None:
            self.session = self.application.session_manager.gen_session()
        self.session.set(k, v)
        return self.application.session_manager.save(self.session)

    def delete_from_session(self, k='uid'):
        try:
            if self.current_user is None:
                return
            self.application.session_manager.delete(self.session)
            return
        except Exception as e:
            self.logger.error(traceback.format_exc())
            return

    def pack_img_url(self, data: [], img_field):
        length = len(data)
        i = 0
        while i < length:
            img_db_url = data[i].get(img_field, None)
            if img_db_url is None:
                break
            data[i][img_field] = config.IMG_URL + img_db_url
            i += 1


class SMSHandler(BaseHandler):
    async def send_msg(self, mobile, prefix=const.redis_prefix_mall_login):
        try:
            ttl = await self.redis.ttl(smsSDK.getSMSKey(mobile, prefix))
            if ttl > sms_config.ALLOW_TTL:
                return self.write_error(errorCode.SEND_SMS_FREQUENCY, errorMsg.SEND_SMS_FREQUENCY_MSG, status_code=200)
        except Exception as e:
            return self.logger.error(f"redis error:{traceback.format_exc()}")

        if await smsSDK.send_message(mobile, prefix):
            return self.write_json()
        else:
            return self.write_error(errorCode.SEND_SMS_ERR, desc=errorMsg.SEND_SMS_ERR_MSG, status_code=200)

        # 使用celery 发短信
        # from servers.celery_server import sms
        # sms.delay(mobile)
        # self.write_json()

    def get_post_sms_code(self):
        dic = util.json_decode(self.request.body)
        return str(dic.get('sms_code', None))

    async def save_img(self, field_name):
        try:
            file = self.request.files.get(field_name)[0]
            self.logger.info(f'file length:{len(file)}')
        except:
            return None
        filename = file['filename']
        content_type = file['content_type']
        body = file['body']
        self.logger.info(f"get_file {filename} {content_type} {len(body)}")

        suffix = content_type.split('/')[-1]
        # 上传图片，返回存储路径
        db_url = "%s.%s" % (util.generate_md5(str(filename)), suffix)
        print(config.BASE_DIRS)
        db_addr = os.path.join(config.BASE_DIRS, 'static', db_url).capitalize()
        print(db_addr)
        self.logger.info(f"upload_fie {filename} {content_type} {len(body)} {db_url} {db_addr}")
        with open(db_addr, 'wb') as f:
            f.write(body)

        return db_url

